/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is the ViewMyCurrency extension.
 *
 * The Initial Developer of the Original Code is
 * Will Moffat.
 * Portions created by the Initial Developer are Copyright (C) 2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

//dump("// $Id: WILL_Helpers.js,v 1.70 2007/02/27 12:29:50 Will Exp $;\n");

////////////////////////////////////////////
// Global Helper functions and variables  //
////////////////////////////////////////////

if (window.VMC) { alert('ViewMyCurrency: Critical Error!\nPlease email vmc_bugs@hamstersoup.com with message:\nGlobal variable VMC is already set to ['+VMC+']') }

var VMC = new function() {
  var self = this;

this.getBrowserWindow = function() {
    return Components.classes["@mozilla.org/appshell/window-mediator;1"].getService(Components.interfaces.nsIWindowMediator).getMostRecentWindow("navigator:browser");
};

// - used by build script to update install.rdf
this.VERSION = 0.3897;  // alpha-version

this._lastHackTime = new Date();

// JS annoyance: why isn't arguments a proper array? I want to do dump(arguments.join(','));
  this.HACK = function(message,m2) {  //NOGREP - don't show in 'make hack'
  m2 = m2 || '';  
  var time = new Date();
  var diff = (time - this._lastHackTime) / 1000; // in seconds
  this._lastHackTime = time;
  dump('HACK: (+'+diff+'s)\t'+message+m2+'\n'); //NOGREP - don't show in 'make hack'
};


this.debugLevel=100;
// This code is independant of my pref funcs so that I can debug them too!
try { 
  this.debugLevel = Components.classes["@mozilla.org/preferences-service;1"].
                   getService(Components.interfaces.nsIPrefService).
                   getBranch('extensions.viewmycurrency.').
                   getCharPref('debug_level');
 } catch (e) { this.debugLevel = 100;}  //Show everything! This will be over-ridden by default prefManager.getDebugLevel()  almost immediately 
if (this.debugLevel>0) { dump('ViewMyCurrency: debug_level='+this.debugLevel+'\n') }

this.shortenStr = function (str,max) {
  if (!str) { str='null'; }
  str=str.replace(/\n/g,' '); //keep it on one line
  return (str.length>max) ? str.substr(0,max-3)+'...' : str;
};

this.ge = function(id,failSilently) {
   dump('HACK: ge:'+id);
  var element=window.document.getElementById(id);
   dump(' '+element+'\n'); //HACK:
  if (!element && !failSilently) { this.log('ERR','Failed to find GUI element '+id); }
  return element;
    
};

this.killChildren = function(n) {
  while (n.firstChild) { n.removeChild(n.firstChild); }
};

this.copyToClipboard = function(text) { //WILL: TODO: there seems to be a bug that newlines are lost when pasting into Notepad. Word is fine.
  if (!text || !text.length) { return; } //nothing to copy
  const clipURI = "@mozilla.org/widget/clipboardhelper;1";
  const clipI = Components.interfaces.nsIClipboardHelper;
  var clipboard = Components.classes[clipURI].getService(clipI);  
  clipboard.copyString(text); //WILL: this could fail? TODO: does it need catch(e)
};



this.setOutline = function(node,style) { 
  var originalOutline;
  originalOutline = node.style.outline;
  node.style.outline = style;
  
  if (!node.VMC_origOutline) {
    if (!originalOutline) { originalOutline = "none"; }
    node.VMC_origOutline = originalOutline; // store the original outline style
  }
};

this.removeOutline = function(node) {
  var originalOutline = node.VMC_origOutline;
  if (!originalOutline) { VMC.log('ERR','no backup outline'); }
  node.style.outline = originalOutline;
};
  

// code based on blink() from Javascript shell
this.blink = function(node,numTimes) {
  if (!numTimes) {numTimes=11}

  for(var i=1; i<=(numTimes*2); ++i) {
    if (i%2) { setTimeout(function() {self.setOutline(node,'4px solid red')}, i*200) }
    else     { setTimeout(function() {self.removeOutline(node)},              i*200) }
  }
};


  //WILL: TODO: debuglevel 1 has no effect
  //WILL: TODO: review use of logging. Make a clear distinction between debugging and user warnings/errors
// If debugLevel is at default of 0 then no messages except warnings or errors should be printed.
this.log = function(severity,message) {
  var alertOfLastResort='ViewMyCurrency: Please email vmc_bugs@hamstersoup.com with message: ';
  if (!severity || !message) { alert(alertOfLastResort+'Missing severity ['+severity+'] or message ['+message+']'); }
  var d=this.debugLevel;
  var label;
  var makeExcp;
  switch(severity) {
  case 'DBG3' : if (d>3) { label='Debug(4): '         + message; }    break; 
  case 'DBG'  : if (d>2) { label='Debug(3): '          + message; }    break;
  case 'INFO' : if (d>1) { label='Info(2): '           + message; }    break;
  case 'WARN' :            label='Warning(0): '        + message + this.getStackTrace();   break;
  case 'ERR'  :            label='Internal Error(0): ' + message + this.getStackTrace();   break;
  default     : alert(alertOfLastResort+' unknown severity '+severity+' set for '+message); 
  }
  if (label) {
    this.log.consoleService.logStringMessage('ViewMyCurrency: '+label);
    if (severity=='ERR') { 
      dump(label); // it was an internal error, always generate an exception and dump to console
      throw label;
    }
  }
};

this.log.consoleService = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);

this.getStackTrace = function(ignoreLines) {
  if (!ignoreLines) { ignoreLines=3; } //default value
  var trace;
  try {throw new Error(); } catch (e) {trace = e.stack; }
  var lines = trace.split('\n');

  //remove the first lines (ingore trace into error funcs)
  lines.splice(0, ignoreLines); 

  trace=lines.join('\n');

  return '\n'+
    '---------------------------- Stack Trace -----------------------------\n'+
    trace + '\n'+
    '----------------------------------------------------------------------\n';

};

// VMC.alert is for internal errors that the user really needs to report to the developer
this.alert = function(message) {
  var label = message+'\n'+this.getStackTrace();
  alert('ViewMyCurrency Internal Error! Please leave a comment at hamstersoup.com with this message:\n'+ label);
};

this.applyFuncToArray = function(func,list) {
  if (!func || !func.length) { VMC.log('ERR','Cannot apply '+func+' to '+list); }
  for (var i=0, len=list.length; i<len; i++) {
    var retVal = func(list[i]);
    if (retVal !== undefined) { return retVal; }
  }
  return undefined;
};

//WILL: Useful constructor code for giving pseudo-unique IDs to each object
//   this.__id = Math.round(Math.random()*100);
//   dump('Created new '+this.__proto__.constructor.name+' # '+this.__id+'\n');
//   this.toString = function() { return this.__proto__.constructor.name + '#'+this.__id; };

this.updateAndAddHandlerToLinks = function() {

  var clickFunc = function(e) {
    var browser = VMC.getBrowserWindow();
    var url     = e.target.getAttribute('href');
    browser.open(url);
  };

  var nodes=document.getElementsByAttribute('class','text-link');
  if (!nodes || !nodes.length) { log('WARN','No links to update'); return; }
  for (var i=0;i<nodes.length;i++) {
    var href=nodes[i].getAttribute('href');
    if (!href) {log('ERR','Element '+nodes[i]+' has no href'); }
    if (href) {
      if      (href.match(/bounce/))  { href += "_ver"+VMC.VERSION;      }  // for hamstersoup.com/ViewMyCurrency/bounce/XXX_verYYY
      else if (href.match(/hamster/)) { href += "?version="+VMC.VERSION; }  // for hamstersoup.com
      nodes[i].setAttribute('href',href);
      nodes[i].addEventListener('click', clickFunc, false);
    }
  }
};



this.escapeUnicode = function(str) {
  var newstr='';
  var code,hex,newchr;
  for (var i=0;i<str.length;i++) {
    code=str.charCodeAt(i);
    if (code<128) {
      newchr=str[i];
    } else {
      hex=code.toString(16);
      if      (code<0x100) { newchr='\\u00'+hex; }
      else if (code<0x1000) { newchr='\\u0'+hex; }
      else if (code<0x10000) { newchr='\\u'+hex; }
      else                   { VMC.alert('Internal error - invalid unicode '+code); }
    }
    newstr=newstr+newchr;
  }
  return newstr;
};

}; // end of VMC

